// ----------------------------------
// RSDK Project: Sonic 1/Sonic 2
// Script Description: Stage Setup Object
// Script Author: Christian Whitehead/Simon Thomley
// Unpacked by Rubberduckycooly's script unpacker
// ----------------------------------

// Aliases
private alias 4 : TYPE_STAGESETUP

private alias object.type 			: player.type
private alias object.controlMode 	: player.controlMode
private alias object.visible 		: player.visible
private alias object.left 			: player.left
private alias object.right 			: player.right
private alias object.xvel 			: player.xvel
private alias object.yvel 			: player.yvel
private alias object.speed			: player.speed
private alias object.xpos 			: player.xpos
private alias object.ypos 			: player.ypos
private alias object.collisionLeft 	: player.collisionLeft
private alias object.value18 		: player.sortedDrawOrder

// Function declarations
reserve function StageSetup_HandleOscillationTable

// Static Values
public value StageSetup_value22 = 0;
public value StageSetup_value21 = 0;

// Used by monitors to determine whether to show static or not and what frame of it to show
private alias StageSetup_value22 : StageSetup_monitorOverlayFrame

private alias StageSetup_value21 : StageSetup_oscillateFlipFlags

// Tables
public table StageSetup_initOscillationTable
	128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0
	128, 0, 0x50F0, 286, 0x2080, 180, 0x3080, 270, 0x5080, 450, 0x7080, 630, 128, 0, 128, 0
end table

public alias StageSetup_oscillationTable : StageSetup_table9

public table StageSetup_oscillationTable
	128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0
	128, 0, 0x50F0, 286, 0x2080, 180, 0x3080, 270, 0x5080, 450, 0x7080, 630, 128, 0, 128, 0
end table

public table StageSetup_oscillateLimits
	2, 0x1000, 2, 0x1800, 2, 0x2000, 2, 0x3000, 4, 0x2000, 8, 0x800,  8, 0x4000, 4, 0x4000
	2, 0x5000, 2, 0x5000, 2, 0x2000, 3, 0x3000, 5, 0x5000, 7, 0x7000, 2, 0x1000, 2, 0x1000
end table


function StageSetup_HandleOscillationTable
	temp0 = 0
	temp1 = 0
	while temp0 < 16
		GetTableValue(temp4, temp1, StageSetup_oscillateLimits)		// temp4 = oscillateSpeed
		GetTableValue(temp6, temp1, StageSetup_oscillationTable)	// temp6 = oscillatePos
		temp1++

		GetTableValue(temp5, temp1, StageSetup_oscillateLimits)		// temp5 = oscillateMax
		GetTableValue(temp7, temp1, StageSetup_oscillationTable)	// temp7 = oscillateInc
		temp1--

		GetBit(temp2, StageSetup_oscillateFlipFlags, temp0)
		if temp2 == false
			temp7 += temp4
			temp6 += temp7
			if temp6 >= temp5
				SetBit(StageSetup_oscillateFlipFlags, temp0, true)
			end if
		else
			temp7 -= temp4
			temp6 += temp7
			if temp6 < temp5
				SetBit(StageSetup_oscillateFlipFlags, temp0, false)
			end if
		end if

		SetTableValue(temp6, temp1, StageSetup_oscillationTable)
		temp1++

		SetTableValue(temp7, temp1, StageSetup_oscillationTable)
		temp1++

		temp0++
	loop
end function


event ObjectMain

	// Only do these actions if the stage is active, of course
	if stage.state == STAGE_RUNNING

		// Do ring animation here
		// -> Not done in the ring script because all rings should share the same frame
		ringTimer++
		if ringTimer == 4
			ringTimer = 0
			ringFrame++
			ringFrame &= 7
		end if


		// Update monitor static
		StageSetup_monitorOverlayFrame++
		if StageSetup_monitorOverlayFrame > 17
			StageSetup_monitorOverlayFrame = 0
		end if

		// Not in time attack
		if options.gameMode != 2
			if player.score >= player.scoreBonus
				// Player achieved a high enough score, give them an extra life
				player.lives++
				player.scoreBonus += 0xC350
				PlaySfx(SfxName[Life], 0)
				PauseMusic()
				ResetObjectEntity(25, TypeName[Music Event], 2, 0, 0)
				object[25].priority = PRIORITY_ACTIVE
			end if
		end if

		// Handle Oscillations & Platform Array movements
		oscillation++
		oscillation &= 511
		CallFunction(StageSetup_HandleOscillationTable)


		// Check for Time Over
		if stage.timeEnabled == true
			if stage.minutes == 10 // At 10 minutes?
				CheckEqual(stage.debugMode, true)
				temp0 = checkResult
				CheckEqual(options.gameMode, 2)
				temp0 |= checkResult
				if temp0 == false
					currentPlayer = 0
					player[0].type = TypeName[Player Object]
					CallFunction(PlayerObject_Kill)
				end if

				// Reset time to 9:59:99 and disable time progression so that it stays that way
				stage.minutes 		= 9
				stage.seconds 		= 59
				stage.milliSeconds 	= 99
				stage.timeEnabled 	= false
			end if
		end if

		// Check for death bounds
		foreach (GROUP_PLAYERS, currentPlayer, ACTIVE_ENTITIES)
			temp0 = player[currentPlayer].collisionLeft
			temp0 <<= 16
			temp0 += player[currentPlayer].xpos
			temp1 = stage.curXBoundary1
			temp1 <<= 16

			if temp0 < temp1
				if player[currentPlayer].right == true
					player[currentPlayer].xvel  = 0x10000
					player[currentPlayer].speed = 0x10000
				else
					player[currentPlayer].xvel  = 0x00000
					player[currentPlayer].speed = 0x00000
				end if

				player[currentPlayer].xpos = temp1

				temp0 = player[currentPlayer].collisionLeft
				temp0 <<= 16

				player[currentPlayer].xpos -= temp0
			end if

			temp1 = stage.curYBoundary2
			temp1 <<= 16

			if temp1 < stage.deathBoundary
				if player[currentPlayer].ypos > stage.deathBoundary
					CallFunction(PlayerObject_Kill)
				end if
			else
				if player[currentPlayer].ypos > temp1
					CallFunction(PlayerObject_Kill)
				end if
			end if
		next

	end if // stage.state == STAGE_RUNNING

	// (Un)Lock player controls based on current situation
	if options.attractMode == false
		if player[0].controlMode > -1
			options.touchControls = true
		else
			options.touchControls = false
		end if
	else
		options.touchControls = 0
	end if

	// Sort players, player 1 should always be on top of player 2
	currentPlayer = playerCount
	currentPlayer--
	while currentPlayer > -1

		// Only do this stuff if the player is visible
		if player[currentPlayer].visible == true

			currentPlayer += playerCount

			if player[currentPlayer].sortedDrawOrder == 0

				currentPlayer -= playerCount
				arrayPos0 = player[currentPlayer].sortedDrawOrder
				AddDrawListEntityRef(arrayPos0, currentPlayer)
				currentPlayer += playerCount
				AddDrawListEntityRef(arrayPos0, currentPlayer)
				currentPlayer -= playerCount

			else

				currentPlayer -= playerCount
				arrayPos0 = player[currentPlayer].sortedDrawOrder
				currentPlayer += playerCount
				AddDrawListEntityRef(arrayPos0, currentPlayer)
				currentPlayer -= playerCount
				arrayPos0 = player[currentPlayer].sortedDrawOrder
				AddDrawListEntityRef(arrayPos0, currentPlayer)

			end if

		end if

		currentPlayer--

	loop

end event


event ObjectStartup

	// Setup the common music tracks used in every level
	SetMusicTrack("ActComplete.ogg", 1, false)
	SetMusicTrack("Invincibility.ogg", 2, 39528)
	SetMusicTrack("Continue.ogg", 3, false)
	SetMusicTrack("Boss.ogg", 4, true)
	SetMusicTrack("GameOver.ogg", 5, false)
	SetMusicTrack("Drowning.ogg", 6, false)
	switch saved.ssmusic
		case 1
			SetMusicTrack("Hyper3.ogg", 7, 1)
			break
		case 3
			SetMusicTrack("Hyperr3K.ogg", 7, 1)
			break
		case 4
			SetMusicTrack("HyperP.ogg", 7, 1)
			break
		case 5
			SetMusicTrack("HyperOrigins.ogg", 7, 1)
			break
	end switch

	// Setup object range
	if options.attractMode == false
		// Blank space intentional, there's code here on the official PC version
		// However! we don't have that, so we're just stuck with this
	else
		// Set Object Range, changes internal bounds so that demos always looks the same and don't desync
		SetObjectRange(424)
	end if

	// Reset the music effect functions
	// They should later get their proper music functions assigned by object 39 (stage-specific setup) but this doesn't hurt
	SpeedUpMusic    = 0
	SlowDownMusic   = 0
	stage.musicFlag = 0

	// Set stage death bounds
	stage.deathBoundary = stage.curYBoundary2
	stage.deathBoundary <<= 16

	// Remove all instances of this object in case one is accidentally placed in the scene
	foreach (TypeName[Stage Setup], arrayPos0, ALL_ENTITIES)
		ResetObjectEntity(arrayPos0, TypeName[Blank Object], 0, 0, 0)
	next

	// Set object slot 8 a Stage Setup object
	object[8].type = TypeName[Stage Setup]
	object[8].priority = PRIORITY_ACTIVE_PAUSED

	foreach (TypeName[HUD], arrayPos0, ALL_ENTITIES)
		// Remove all HUD objects in case one is placed in the scene
		object[arrayPos0].type = TypeName[Blank Object]

		if credits.screen == false
			// Unless in the credits, place the HUD object in slot 9
			object[9].type = TypeName[HUD]
			object[9].priority = PRIORITY_ACTIVE
			object[9].drawOrder = 6
			object[9].propertyValue = object[arrayPos0].propertyValue
		end if
	next


	// Reset more variables
	ringExtraLife 					= 100
	oscillation 					= 0
	StageSetup_oscillateFlipFlags 	= 0b0011111000000000


	temp0 = 0
	while temp0 < 32
		GetTableValue(temp1, temp0, StageSetup_initOscillationTable)
		SetTableValue(temp1, temp0, StageSetup_oscillationTable)
		temp0++
	loop

	// Bonus options can only be used in no save mode
	if options.saveSlot > 0
		options.shieldType  = 0
		options.superStates = 0
	end if

	// Set the effects based on shield prefrence
	switch options.shieldType
	case 0
	case 2
		blueShieldType = TypeName[Blue Shield]
		invincibilityType = TypeName[Invincibility]
		break

	case 1
	case 3
		blueShieldType = TypeName[Blue Shield 2]
		invincibilityType = TypeName[Invincibility 2]
		break

	end switch

end event

event RSDKDraw
	DrawSprite(0)
end event

event RSDKLoad
	LoadSpriteSheet("Global/Display.gif")
	SpriteFrame(-16, -16, 32, 32, 1, 143)
end event
